import { Sequelize, sequelize, Document, Task, Progress, User, UserGroup } from '../models'
import { createProgresses } from './group_progress_mq'
import { sendNotifications } from './notification'
import _ from 'lodash'
import moment from 'moment'
import { findAllTaskProgresses } from './progress'
import EVENTS from '../events'
import CONFIG from '../config'
import axios from 'axios'
/**
 * Create a new task instance of a document
 * @param {int} documentId
 * @param {int} userId
 * @param {int} groupId
 * @return {object} Tasks
 */
export const findOrCreate = async ({
  title = '',
  type = 'liberty',
  documentId,
  userId,
  groupId,
  textbookId = 0,
  courseId = 0,
  createdAt = '',
  endAt = '',
  prePublishAt = '',
  transaction = null
}) => {
  let doc = null
  if (type == 'liberty') {
    doc = await genDoc({documentId, transaction})
  }
  // TODO: add user progress merge
  // Find existing task of the document version for the group or user
  let condition = { versionId: documentId }
  if (groupId && groupId > 0) {
    condition.groupId = groupId
  } else {
    condition.userId = userId
  }
  let task = await Task.findOne(
    {
      where: condition
    },
    { transaction }
  )
  if (task) {
    return task
  }
  // Create a new task if not found
  task = {}
  task.ownerId = userId
  if (endAt) {
    task.expireAt = endAt
  }
  task.name = title || doc.name || ''
  task.type = type
  task.textbookId = textbookId
  task.courseId = courseId
  task.userId = userId
  task.documentId = documentId
  task.versionId = documentId
  task.groupId = groupId
  if (createdAt) {
    task.createdAt = moment(createdAt).toDate()
  }
  if (prePublishAt) {
    task.prePublishAt = prePublishAt
  }
  task = await Task.create(task, { transaction })
  if (task.groupId) {
    let isPublishImd = !prePublishAt
    await bulkCreateProgress({taskId: task.id, isPublishImd, transaction})
  }
  if (!prePublishAt) {
    await publish(task.id)
  }
  return task
}

export const create = async ({
  documentId,
  userId,
  groupId,
  endAt = null,
  prePublishAt = null
}) => {
  task = {}
  task.ownerId = userId
  task.expireAt = endAt
  task.documentId = documentId
  task.groupId = groupId
  task.prePublishAt = prePublishAt

  task = await Task.create(task)

  await bulkCreateProgress({taskId: task.id, transaction})

  if (!prePublishAt) {
    await publish(task.id)
  }
  return task
}

export const bulkCreateProgress = async ({taskId, transaction = null, isPublishImd = true}) => {
  let task = await Task.findById(taskId)
  if (!task.groupId) return false
  let opt = {}
  if (transaction) opt.transaction = transaction
  let ugs = await UserGroup.findAll({
    where: {
      groupId: task.groupId
    }
  })
  if (!ugs || ugs.length == 0) return

  let existedProgresses = await Progress.findAll({
    attributes: ['userId'],
    where: {
      taskId
    }
  })

  let existedUserIds = existedProgresses.map(n => n.userId)

  let progressDates = []
  ugs.forEach(n => {
    if (existedUserIds.indexOf(n.userId) >= 0) return
    progressDates.push({
      type: task.type || 'liberty',
      name: task.name || '',
      subTaskCount: task.subTaskCount,
      expireAt: task.expireAt,
      state: 'start',
      userId: n.userId,
      taskId: taskId,
      isPublished: 0,
      documentId: task.documentId,
      versionId: task.versionId,
      groupId: task.groupId
    })
  })
  task.userCount = progressDates.length
  await task.save(opt)

  let rs = await Progress.bulkCreate(progressDates, opt)
  return rs
}

/**
 * Delete a task
 * @param {int} taskId
 * @param {int} userId
 * @return {object} Tasks
 */
export const remove = async (taskId, userId) => {
  let task = await Task.findById(taskId)
  if (task == null) {
    throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}`)
  }
  if (userId && task.ownerId != userId) {
    throw new Error(`${ERRORS.DBERR.ERR_UNAUTHORIZED_OPERATION}`)
  }
  return await task.destroy()
}

/**
 * List all tasks for the document and owner
 * Order by updateAt
 * @param {int} documentId
 * @param {int} ownerId
 * @param {int} pageIndex
 * @param {int} pageSize
 * @return {object} Tasks
 */
export const findAllDocumentTasks = async (
  documentId,
  ownerId,
  pageIndex = 0,
  pageSize = 20
) => {
  const result = await Task.findAndCountAll({
    where: {
      documentId,
      ownerId
    },
    offset: pageIndex * pageSize,
    limit: pageSize,
    order: [['updatedAt', 'DESC']]
  })
  return result
}

/**
 * Send notifications to unfinished users in the same group of the task
 * @param {int} taskId
 * @return {object} Tasks
 */
export const urge = async (taskId, userId) => {
  let task = await Task.findById(taskId)
  if (task == null) {
    throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}`)
  }
  if (userId && task.ownerId != userId) {
    throw new Error(`${ERRORS.DBERR.ERR_UNAUTHORIZED_OPERATION}`)
  }
  let progresses = await Progress.findAll({
    where: {
      taskId,
      state: 'start'
    },
    order: [['updatedAt', 'DESC']]
  })

  let targets = progresses.map(progress => {
    return {
      userId: progress.userId,
      targetId: progress.id,
      scope: 'progress'
    }
  })

  let document = await Document.findById(task.versionId)
  let title = document ? document.name : ''
  let user = await User.findById(task.ownerId)

  let userIds = await sendNotifications(
    {
      title: '作业未完成提醒',
      data: {
        keyword1: {
          value: title
        },
        keyword2: {
          value: user.nickName
        }
      },
      url: ({ targetId }) => '/pages/progress/main?isShowSideMenu=y&progressId=' + targetId
    },
    targets
  )

  return User.findAll({
    where: {
      id: { $in: userIds }
    }
  })
}

/**
 * 发布作业
 * @param  {[type]} taskId [description]
 * @return {[type]}        [description]
 */
export const publish = async (taskId, transaction = null) => {
  let opt = {}
  if (transaction) opt.transaction = transaction
  await Task.update({
    isPublished: 1
  }, {
    where: {
      id: taskId
    }
  }, opt)
  await Progress.update({
    isPublished: 1
  }, {
    where: {
      taskId
    }
  }, opt)
  EVENTS.emit('task-publish', {taskId})
  let task = await Task.findById(taskId)
  return task
}

/**
 * 作业到期通知到老师提醒其可以批改了
 * @return {Promise} [description]
 */
export const notifyWhenExpire = async () => {
  let tasks = await Task.findAll({
    where: {
    }
  })
}

export const publishHomework = async ({
  userId,
  title,
  groupIds,
  prePublishAt = '',
  endAt = '',
  textbookId,
  courseId,
  exercises,
  descriptionBlocks = [],
  transaction = null
}) => {
  // 保存作业数据
  let apiUrl = `${CONFIG.app.contentServerApi}/v1/assignments`
  let res = await axios({
    method: 'post',
    url: apiUrl,
    headers: {
      'Authorization': 'Bearer 1EB39DDA-CA30-458F-9E13-BDE9CE2B5A12'
    },
    data: {
      assignment: {
        descriptionBlocks,
        id: 0,
        teacherId: userId,
        title
      },
      classIds: groupIds,
      courseId,
      exercises,
      textbookId
    }
  })
  if (!res.data.createdAssignmentId) {
    throw new Error('assignment id error!!')
  }

  let createdAssignmentId = res.data.createdAssignmentId

  let tasks = []
  for (let i = 0; i < groupIds.length; i++) {
    let gid = groupIds[i]
    if (!gid) continue
    let task = await findOrCreate({
      title,
      type: 'exercise',
      documentId: createdAssignmentId,
      userId,
      groupId: gid,
      endAt,
      prePublishAt,
      transaction
    })
    tasks.push(task)
  }
  return tasks
}

const genDoc = async ({documentId, transaction = null}) => {
  if (!documentId) {
    throw new Error(`${ERRORS.ERR_INVALID_PARAMETER}`)
  }
  let doc = await Document.findById(documentId, {
    transaction
  })
  if (doc == null) {
    throw new Error(`${ERRORS.DBERR.ERR_RECORD_NOT_FOUND}`)
  }
  let version = null
  if (doc.currentVersionId) {
    version = await Document.findById(doc.currentVersionId, {
      transaction,
      paranoid: false
    })
  }
  /*
* Check if the document is updated from previous version
* If yes, create a new version then use new version as instance
*/
  // TODO: recursive versioning for document folders
  // if (version == null || !_.isEqual(doc.data, version.data)) {
  doc.version++
  version = _.cloneDeep(doc.dataValues)
  delete version.id
  version.documentId = doc.id
  version = await Document.create(version, {
    transaction
  })
  doc.currentVersionId = version.id
  doc.updatedAt = version.updatedAt
  await doc.save({ transaction })
  // }
  return doc
}
